#include "deviceuiwidget.h"
#include "ui_deviceuiwidget.h"

#include "adb/adbprocess.h"
#include "utils/utils.h"

#include <QImage>
#include <QPixmap>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QResizeEvent>
#include <QXmlStreamReader>
#include <QClipboard>
#include <QDebug>

#include <QtConcurrent>
#include <QFutureWatcher>

DeviceUiWidget::DeviceUiWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::DeviceUiWidget)
    , m_pScene(nullptr)
    , m_pPixmapItem(nullptr)
    , m_ratio(0)
{
    this->setAttribute(Qt::WA_DeleteOnClose);
    ui->setupUi(this);

    // 样式表
    ui->table_node->setEditTriggers(QAbstractItemView::NoEditTriggers);          // 禁止编辑
    ui->table_node->setSelectionBehavior(QAbstractItemView::SelectRows);         // 整行选中的方式
    ui->table_node->setSelectionMode(QAbstractItemView::SingleSelection);        // 选中模式为单行选中
    ui->table_node->setFocusPolicy(Qt::NoFocus);                                 // 不显示选中虚线
    ui->table_node->verticalHeader()->hide();                                    // 隐藏垂直表头
    ui->table_node->horizontalHeader()->setStretchLastSection(true);             // 最后一行自适应表格
    ui->table_node->horizontalHeader()->setHighlightSections(false);             // 点击表头时不对表头光亮
    ui->table_node->setFont(qApp->font());
    ui->table_node->setColumnWidth(0, 160);

    ui->lab_img->setVisible(false);
    ui->tree_by->setHeaderHidden(true);

    // 显示图片区域
    m_pPixmapItem = new QGraphicsPixmapItem();
    m_pScene = new QGraphicsScene();
    m_pScene->addItem(m_pPixmapItem);
    ui->view_img->setScene(m_pScene);
    ui->view_img->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->view_img->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->view_img->installEventFilter(this);
}

DeviceUiWidget::~DeviceUiWidget()
{
    qDebug() << "~DeviceUiDialog()";
    delete ui;
}

void DeviceUiWidget::setDeviceInfo(const QVariantMap &deviceInfo)
{
    m_deviceId = deviceInfo.value("sn").toString();
    QString title = QString("设备UI布局 - %1 - %2 - %3 - %4").arg(
                        deviceInfo.value("brand").toString(),
                        deviceInfo.value("model").toString(),
                        deviceInfo.value("name").toString(),
                        m_deviceId);
    setWindowTitle(title);

    // 加载UI布局
    on_btn_load_clicked();
}

bool DeviceUiWidget::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::MouseButtonPress)
    {
        if (ui->btn_load->isEnabled())
        {
            QMouseEvent *e = static_cast<QMouseEvent *>(event);
            QPoint point = e->pos();
            QPointF pointF = ui->view_img->mapToScene(point);

            QPoint pixmapPoint(pointF.x() / m_ratio, pointF.y() / m_ratio);
            QString text = QString("(%1,%2)").arg(QString::number(pixmapPoint.x()), QString::number(pixmapPoint.y()));
            ui->lab_pos->setText(text);
            selectItemByPoint(pixmapPoint);
        }
    }
    return QObject::eventFilter(watched, event);
}

void DeviceUiWidget::resizeEvent(QResizeEvent *)
{
    // 自适应缩放
    const QPixmap *pixmap = ui->lab_img->pixmap();
    if (pixmap == nullptr || pixmap->isNull())
    {
        return;
    }
    pictureAdaptiveSize(pixmap->copy());
}

void DeviceUiWidget::on_btn_load_clicked()
{
    loadDeviceUi();
}

void DeviceUiWidget::pictureAdaptiveSize(const QPixmap &pixmap)
{
    double iw = pixmap.width();
    double ih = pixmap.height();
    double vw = ui->view_img->width();
    double vh = ui->view_img->height();
    int width, height;
    if (vw / iw > vh / ih)
    {
        m_ratio = vh / ih;
        width = m_ratio * iw;
        height = vh;
    }
    else
    {
        m_ratio = vw / iw;
        width = vw;
        height = m_ratio * ih;
    }
    m_pPixmapItem->setPixmap(pixmap.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation));
}

void DeviceUiWidget::parseDumpXml(const QString &dumpFile)
{
    QFile file(dumpFile);
    file.open(QIODevice::ReadOnly);

    QDomDocument *doc = new QDomDocument();
    if (!doc->setContent(&file))
    {
        file.close();
        return;
    }
    file.close();

    QDomElement docElem = doc->documentElement();
    QTreeWidgetItem *it = new QTreeWidgetItem();
    it->setText(0, docElem.tagName());
    ui->tree_by->addTopLevelItem(it);

    showXmlTreeNode(it, docElem.firstChild());
}

void DeviceUiWidget::showXmlTreeNode(QTreeWidgetItem *treeParent, QDomNode domParent)
{
    while (!domParent.isNull())
    {
        QTreeWidgetItem *it = new QTreeWidgetItem();

        // 获取属性值
        QVariantMap dataMap;
        QDomNamedNodeMap map  = domParent.attributes();
        for (int i = 0; i < map.count(); i++)
        {
            QDomNode debug = map.item(i);
            QDomAttr attr = debug.toAttr();
            QString name = attr.name();      // 获得属性名;
            QString value = attr.value();    // 获得属性值;
            dataMap.insert(name, value);
        }
        it->setData(0, 999, dataMap);

        QString index = dataMap.value("index").toString();
        QString clazz = dataMap.value("class").toString();
        QString text = dataMap.value("text").toString();
        QString desc = dataMap.value("content-desc").toString();
        QString bounds = dataMap.value("bounds").toString();
        it->setText(0, QString("(%1) %2:%3 %4 %5").arg(
                        index,
                        clazz.replace("android.widget.", ""),
                        text,
                        QString("{%1}").arg(desc),
                        bounds));
        treeParent->addChild(it);
        if (domParent.hasChildNodes())
        {
            showXmlTreeNode(it, domParent.firstChild());
        }
        domParent = domParent.nextSibling();
    }
}

void DeviceUiWidget::selectItemByPoint(const QPoint &point)
{
    // 查找符合条件的item
    QTreeWidgetItem *minItem = nullptr;
    QRect minRect;
    QTreeWidgetItemIterator it(ui->tree_by);
    while (*it)
    {
        QTreeWidgetItem *item = (*it);
        QVariantMap map = item->data(0, 999).toMap();
        QString bounds = map.value("bounds").toString();
        bounds = bounds.replace("][", ",").replace("[", "").replace("]", "");
        QStringList bs = bounds.split(",");
        int left = bs.value(0).toInt();
        int top = bs.value(1).toInt();
        int width = bs.value(2).toInt() - bs.value(0).toInt();
        int height = bs.value(3).toInt() - bs.value(1).toInt();
        QRect rect(left, top, width, height);
        if (rect.contains(point))
        {
            if (minRect.isNull())
            {
                minRect = rect;
                minItem = item;
            }
            else
            {
                if (minRect.contains(rect))
                {
                    minRect = rect;
                    minItem = item;
                }
            }
        }
        ++it;
    }
    if (minItem)
    {
        ui->tree_by->setCurrentItem(minItem);
        ui->tree_by->expandItem(minItem);
        on_tree_by_itemClicked(minItem, 0);
    }
}

void DeviceUiWidget::painterTextPixmap(QPixmap &pixmap)
{
    QPainter painter(&pixmap);
    painter.setBrush(Qt::transparent);
    painter.setPen(Qt::black);

    QTreeWidgetItemIterator it(ui->tree_by);
    while (*it)
    {
        QTreeWidgetItem *item = (*it);
        QVariantMap map = item->data(0, 999).toMap();
        QString bounds = map.value("bounds").toString();
        bounds = bounds.replace("][", ",").replace("[", "").replace("]", "");
        QStringList bs = bounds.split(",");
        int left = bs.value(0).toInt();
        int top = bs.value(1).toInt();
        int width = bs.value(2).toInt() - bs.value(0).toInt();
        int height = bs.value(3).toInt() - bs.value(1).toInt();
        QRect rect(left, top, width, height);

        QString clazz = map.value("class").toString();
        if (clazz == "android.widget.ImageView" && (left != 0 || top != 0))
        {
            painter.fillRect(rect - QMargins(3, 3, 3, 3), Qt::lightGray);
        }
        else if (clazz == "android.view.View")
        {
            painter.drawRect(rect);
        }
        else if (clazz == "android.widget.RadioButton")
        {
            painter.drawEllipse(rect - QMargins(width / 4, height / 4, width / 4, height / 4));
            if (map.value("checked").toBool())
            {
                painter.save();
                painter.setBrush(Qt::blue);
                painter.drawEllipse(rect - QMargins(width / 3, height / 3, width / 3, height / 3));
                painter.restore();
            }
        }
        else if (clazz == "android.widget.Switch")
        {
            painter.drawRoundRect(rect, 20, 20);
            if (map.value("checked").toBool())
            {
                painter.save();
                painter.setBrush(Qt::blue);
                painter.drawRoundRect(rect - QMargins(width / 5, height / 5, width / 5, height / 5), 20, 20);
                painter.restore();
            }
        }

        QString text = map.value("text").toString();
        if (!text.isEmpty())
        {
            QString package = map.value("package").toString();
            if (package.endsWith(".launcher"))
            {
                painter.fillRect(rect, Qt::lightGray);

                QFont font;
                font.setPixelSize(36);
                painter.setFont(font);
                painter.drawText(rect, Qt::AlignHCenter | Qt::AlignBottom, text);
            }
            else
            {
                int pixelSize = height * 0.7;
                if (pixelSize <= 0)
                {
                    pixelSize = 36;
                }
                QFont font;
                font.setPixelSize(pixelSize);
                painter.setFont(font);
                painter.drawText(rect, text);
            }
        }
        ++it;
    }
}

void DeviceUiWidget::loadDeviceUi()
{
    if (!AdbProcess::IsDeviceMode(m_deviceId))
    {
        return;
    }

    ui->tree_by->clear();
    ui->table_node->clearContents();
    ui->table_node->setRowCount(0);
    ui->lab_img->setPixmap(QPixmap());
    ui->btn_load->setEnabled(false);

    QFuture<void> future = QtConcurrent::run([](const QString & deviceId)
    {
        AdbProcess::Screencap(deviceId, "/data/local/tmp/screen.png");
        AdbProcess::UiautomatorDump(deviceId, "/data/local/tmp/dump.xml");

        AdbProcess::Pull(deviceId, "/data/local/tmp/screen.png", PathUtil::tmpScreenFile(deviceId));
        AdbProcess::Pull(deviceId, "/data/local/tmp/dump.xml", PathUtil::tmpDumpFile(deviceId));

        AdbProcess::Rm(deviceId, "/data/local/tmp/screen.png");
        AdbProcess::Rm(deviceId, "/data/local/tmp/dump.xml");
    }, m_deviceId);

    QFutureWatcher<void> *pWatcher = new QFutureWatcher<void>;
    pWatcher->setFuture(future);
    connect(pWatcher, &QFutureWatcher<void>::finished, this, [ = ]
    {
        QString dumpFile = PathUtil::tmpDumpFile(m_deviceId);
        QString screenFile = PathUtil::tmpScreenFile(m_deviceId);

        parseDumpXml(dumpFile);
        QPixmap pixmap(screenFile);

        if (pixmap.isNull())
        {
            // 获取屏幕大小
            QSize size = AdbProcess::GetSize(m_deviceId);

            // 可能是系统禁用截图了，所以用一张纯白图片代替吧
            pixmap = QPixmap(size);
            pixmap.fill(Qt::white);
            painterTextPixmap(pixmap);
        }
        ui->lab_img->setPixmap(pixmap);
        pictureAdaptiveSize(pixmap.copy());

        FileUtil::removeFile(screenFile);
        FileUtil::removeFile(dumpFile);

        ui->btn_load->setEnabled(true);
    });
}

void DeviceUiWidget::on_edit_search_textChanged(const QString &arg)
{
    if (arg.isEmpty())
    {
        QTreeWidgetItemIterator it(ui->tree_by);
        while (*it)
        {
            QTreeWidgetItem *item = (*it);
            item->setBackgroundColor(0, Qt::white);
            ++it;
        }
    }
    else
    {
        QTreeWidgetItemIterator it(ui->tree_by);
        while (*it)
        {
            QTreeWidgetItem *item = (*it);
            QVariantMap map = item->data(0, 999).toMap();
            QString clazz = map.value("class").toString();
            QString resourceId = map.value("resource-id").toString();
            QString text = map.value("text").toString();
            QString desc = map.value("content-desc").toString();
            item->setBackgroundColor(0, (clazz.contains(arg) || resourceId.contains(arg) || text.contains(arg) || desc.contains(arg)) ? QColor("#FFFF99") : Qt::white);
            ++it;
        }
    }
}

void DeviceUiWidget::on_btn_expand_clicked()
{
    ui->tree_by->expandAll();
}

void DeviceUiWidget::on_btn_shou_clicked()
{
    ui->tree_by->collapseAll();
}

void DeviceUiWidget::on_table_node_itemDoubleClicked(QTableWidgetItem *item)
{
    QApplication::clipboard()->setText(item->text());
}

void DeviceUiWidget::on_tree_by_itemClicked(QTreeWidgetItem *item, int )
{
    static QStringList orders =
    {
        "index", "text", "resource-id", "class", "package", "content-desc",
        "checkable", "checked", "clickable", "enabled", "focusable", "focused",
        "scrollable", "long-clickable", "password", "selected", "bounds"
    };

    // table 中显示详细属性
    QVariantMap map = item->data(0, 999).toMap();
    ui->table_node->clearContents();
    ui->table_node->setRowCount(0);
    for (int index = 0; index < orders.count(); index++)
    {
        ui->table_node->insertRow(index);
        QString name = orders[index];
        ui->table_node->setItem(index, 0, new QTableWidgetItem(name));
        ui->table_node->setItem(index, 1, new QTableWidgetItem(map.value(name).toString()));
    }

    // 图片中绘制
    QString bounds = map.value("bounds").toString();
    bounds = bounds.replace("][", ",").replace("[", "").replace("]", "");
    QStringList bs = bounds.split(",");
    int left = bs.value(0).toInt();
    int top = bs.value(1).toInt();
    int width = bs.value(2).toInt() - bs.value(0).toInt();
    int height = bs.value(3).toInt() - bs.value(1).toInt();
    QRect rect(left, top, width, height);

    const QPixmap *pixmap = ui->lab_img->pixmap();
    if (pixmap == nullptr)
    {
        return;
    }
    // 绘制红框
    QPixmap copyPixmap = pixmap->copy();
    QPainter painter(&copyPixmap);
    painter.setPen(QPen(Qt::red, 3));
    painter.drawRect(rect);
    pictureAdaptiveSize(copyPixmap);
}

void DeviceUiWidget::on_btn_open_clicked()
{
    AdbProcess::EnableShowTouches(m_deviceId, true);
    AdbProcess::EnablePointerLocation(m_deviceId, true);
}

void DeviceUiWidget::on_btn_close_clicked()
{
    AdbProcess::EnableShowTouches(m_deviceId, false);
    AdbProcess::EnablePointerLocation(m_deviceId, false);
}
